package edu.hawaii.examschd;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.StringTokenizer;
import edu.hawaii.examschd.modules.Module;

/**
 * The Server class will server HTML requests from a web browser.
 * 
 * @author John MacMillan
 * @author Jayson Gamiao
 * 
 */
public class Server extends Thread {
  static final String HTML_START = "<html>" + "<title>University of Hawaii Exam Scheduler</title>"
      + "<body>";
  static final String HTML_END = "</body>" + "</html>";
  static final String MAIN_FORM =
      "<form action=\"http://127.0.0.1:25009\" enctype=\"multipart/form-data\" method=\"post\">"
          + "<fieldset><legend>View the final exam schedule as follows:</legend>"
          + "<br><h3>List View</h3><br>"
          + "<a href=\"./Library/WebServer/Documents/\">List By Subject</a><br>"
          + "<a href=\"./Library/WebServer/Documents/\">List By Date</a><br>"
          + "<br><h3>Course View</h3><br>" + "<p><b>Subject:</b>" + "<select name=\"subject\">"
          + "<option value=\"ACM\">Academy for Creative Media</option>"
          + "<option value=\"ACC\">Accounting</option>"
          + "<option value=\"AS\">Aerospace Studies</option>"
          + "<option value=\"AMST\">American Studies</option>"
          + "<option value=\"ANAT\">Anatomy</option>"
          + "<option value=\"ANSC\">Animal Science</option>"
          + "<option value=\"ANTH\">Anthropology</option>"
          + "<option value=\"APDM\">Apparel Prod Desgn & Merchandg</option>"
          + "<option value=\"ARAB\">Arabic</option>"
          + "<option value=\"ARCH\">Architecture</option>" + " <option value=\"ART\">Art</option>"
          + "<option value=\"ASAN\">Asian Studies</option>"
          + "<option value=\"ASTR\">Astronomy</option>"
          + "<option value=\"BIOC\">Biochemistry</option>"
          + "<option value=\"BE\">Bioengineering</option>"
          + "<option value=\"BIOL\">Biology</option>"
          + "<option value=\"BIOM\">Biomedical Science</option>"
          + "<option value=\"BOT\">Botany</option>" + " <option value=\"BUS\">Business</option>"
          + "<option value=\"BLAW\">Business Law</option>"
          + "<option value=\"CAM\">Cambodian</option>"
          + "<option value=\"CMB\">Cell and Molecular Biology</option>"
          + "<option value=\"CHAM\">Chamorro</option>"
          + "<option value=\"CHEM\">Chemistry</option>"
          + "<option value=\"CHN\">Chinese Language & Literature</option>"
          + "<option value=\"CEE\">Civil & Environmental Engr</option>"
          + "<option value=\"COM\">Communication</option>"
          + "<option value=\"CIS\">Communication & Info Sciences</option>"
          + "<option value=\"CSD\">Communication Sci & Disorders</option>"
          + "<option value=\"COMG\">Communicology</option>"
          + "<option value=\"CAAM\">Complementary & Alt Medicine</option>"
          + "<option value=\"EDCS\">Curriculum Studies</option>"
          + "<option value=\"DNCE\">Dance</option>"
          + "<option value=\"DH\">Dental Hygiene</option>"
          + "<option value=\"DRB\">Developmental & Repro Biology</option>"
          + "<option value=\"DIS\">Disability Studies</option>"
          + "<option value=\"EALL\">East Asian Languages & Lit</option>"
          + "<option value=\"ECON\">Economics</option>"
          + "<option value=\"EDEA\">Educational Administration</option>"
          + "<option value=\"EDEF\">Educational Foundations</option>"
          + "<option value=\"EDEP\">Educational Psychology</option>"
          + "<option value=\"ETEC\">Educational Technology</option>"
          + "<option value=\"EE\"> Electrical Engineering</option>"
          + "<option value=\"ENGR\">Engineering, Interdisciplinary</option>"
          + "<option value=\"ENG\">English</option>"
          + "<option value=\"ELI\">English Language Institute</option>"
          + "<option value=\"ES\">Ethnic Studies</option>"
          + "<option value=\"FMCH\">Family Medicine & Comm Health</option>"
          + "<option value=\"FAMR\">Family Resources</option>"
          + "<option value=\"FIL\">Filipino</option>" + "<option value=\"FIN\">Finance</option>"
          + "<option value=\"FSHN\">Food Science & Human Nutrition</option>"
          + "<option value=\"FR\">French</option>" + "<option value=\"GEOG\">Geography</option>"
          + "<option value=\"GG\">Geology & Geophysics</option>"
          + "<option value=\"GERI\">Geriatric Medicine</option>"
          + "<option value=\"GER\">German</option>"
          + "<option value=\"GHPS\">Global Health & Pop Studies</option>"
          + "<option value=\"GRK\">Greek</option>" + "<option value=\"HAW\">Hawaiian</option>"
          + "<option value=\"HWST\">Hawaiian Studies</option>"
          + "<option value=\"HNDI\">Hindi</option>" + "<option value=\"HIST\">History</option>"
          + "<option value=\"HON\">Honors</option>"
          + "<option value=\"HRM\">Human Resources Mgt</option>"
          + "<option value=\"ILO\">Ilokano</option>"
          + "<option value=\"IP\">Indo-Pacific Languages</option>"
          + "<option value=\"IND\">Indonesian</option>"
          + "<option value=\"ITM\">Information Technology Mgt</option>"
          + "<option value=\"ICS\">Information & Computer Sciences</option>"
          + "<option value=\"ITE\">Institute for Teacher Educatn</option>"
          + "<option value=\"INS\">Insurance</option>"
          + "<option value=\"IS\">Interdisciplinary Studies</option>"
          + "<option value=\"CUL\">Internatl Cultural Studies</option>"
          + "<option value=\"ITAL\">Italian</option>"
          + "<option value=\"JPN\">Japanese Language & Literature</option>"
          + "<option value=\"JOUR\">Journalism</option>"
          + "<option value=\"KRS\">Kinesiology & Rehab Science</option>"
          + "<option value=\"KOR\">Korean</option>"
          + "<option value=\"LLEA\">Lang & Lit Europe & Americas</option>"
          + "<option value=\"LATN\">Latin</option>"
          + "<option value=\"LAIS\">Latin Amer & Iberian Studies</option>"
          + "<option value=\"LAW\">Law</option>"
          + "<option value=\"LWEV\">Law-Environmental</option>"
          + "<option value=\"LWJT\">Law-Journals and Teams</option>"
          + "<option value=\"LWLW\">Law-Legal Writing</option>"
          + "<option value=\"LWLM\">Law-LLM Master of Laws</option>"
          + "<option value=\"LWPA\">Law-Pacific and Asian</option>"
          + "<option value=\"LWUL\">Law-Ulu Lehua</option>"
          + "<option value=\"LIS\">Library & Information Science</option>"
          + "<option value=\"LING\">Linguistics</option>"
          + "<option value=\"MGT\">Management</option>" + "<option value=\"MAO\">Maori</option>"
          + "<option value=\"MKT\">Marketing</option>"
          + "<option value=\"MATH\">Mathematics</option>"
          + "<option value=\"ME\">Mechanical Engineering</option>"
          + "<option value=\"MDED\">Medical Education</option>"
          + "<option value=\"MEDT\">Medical Technology</option>"
          + "<option value=\"MED\">Medicine</option>"
          + "<option value=\"MET\">Meteorology</option>"
          + "<option value=\"MICR\">Microbiology</option>"
          + "<option value=\"MSL\">Military Science & Leadership</option>"
          + "<option value=\"MCB\">Molecular and Cell Biology</option>"
          + "<option value=\"MBBE\">Molecular Biosci & Bioengr</option>"
          + "<option value=\"MUS\">Music</option>"
          + "<option value=\"NHH\">Native Hawaiian Health</option>"
          + "<option value=\"NREM\">Natural Res & Environmtl Mgt</option>"
          + "<option value=\"NURS\">Nursing</option>"
          + "<option value=\"OBGN\">Obstetrics & Gynecology</option>"
          + "<option value=\"ORE\">Ocean & Resources Engineering</option>"
          + "<option value=\"OCN\">Oceanography</option>"
          + "<option value=\"PACS\">Pacific Islands Studies</option>"
          + "<option value=\"PATH\">Pathology</option>"
          + "<option value=\"PACE\">Peace and Conflict Education</option>"
          + "<option value=\"PED\">Pediatrics</option>"
          + "<option value=\"PHRM\">Pharmacology</option>"
          + "<option value=\"PHIL\">Philosophy</option>"
          + "<option value=\"PHYS\">Physics</option>"
          + "<option value=\"PHYL\">Physiology</option>"
          + "<option value=\"PEPS\">Plant & Environmtl Protect Sci</option>"
          + "<option value=\"POLS\">Political Science</option>"
          + "<option value=\"PORT\">Portuguese</option>"
          + "<option value=\"PSTY\">Psychiatry</option>"
          + "<option value=\"PSY\">Psychology</option>"
          + "<option value=\"PUBA\">Public Administration</option>"
          + "<option value=\"PH\">Public Health</option>"
          + "<option value=\"PPC\">Public Policy Center</option>"
          + "<option value=\"RE\">Real Estate</option>" + "<option value=\"REL\">Religion</option>"
          + "<option value=\"RUS\">Russian</option>" + "<option value=\"SAM\">Samoan</option>"
          + "<option value=\"SNSK\">Sanskrit</option>"
          + "<option value=\"SLS\">Second Language Studies</option>"
          + "<option value=\"SOCS\">Social Science</option>"
          + "<option value=\"SW\">Social Work</option>"
          + "<option value=\"SOC\">Sociology</option>" + "<option value=\"SPAN\">Spanish</option>"
          + "<option value=\"SPED\">Special Education</option>"
          + "<option value=\"SURG\">Surgery</option>" + "<option value=\"TAHT\">Tahitian</option>"
          + "<option value=\"THAI\">Thai</option>" + "<option value=\"THEA\">Theatre</option>"
          + "<option value=\"TONG\">Tongan</option>"
          + "<option value=\"TI\">Translation & Interpretation</option>"
          + "<option value=\"TIM\">Travel Industry Management</option>"
          + "<option value=\"TRMD\">Tropical Med & Medcl Micro</option>"
          + "<option value=\"TPSS\">Tropical Plant & Soil Sciences</option>"
          + "<option value=\"PLAN\">Urban & Regional Planning</option>"
          + "<option value=\"URDU\">Urdu</option>" + "<option value=\"VIET\">Vietnamese</option>"
          + "<option value=\"WS\">Women's Studies</option>"
          + "<option value=\"ZOOL\">Zoology</option>" + "</select></p>" + "</fieldset>"
          + "<input value=\"Submit\" type=\"submit\">" + "</form>";

  private int port; // port we are going to listen to.
  private int count = 0; // server access count.

  BufferedReader inFromClient = null;
  DataOutputStream outToClient = null;
  Socket connectedClient = null;

  /**
   * Constructor
   * 
   * @param listen_port Port for server to listen on.
   */
  public Server(int listen_port) {
    this.port = listen_port;
    this.start();
  }

  public Server(Socket client) {
    this.connectedClient = client;
  }

  /**
   * Allows you to create a server with just this java file.
   * 
   * @param args user input, should contain only the integer of the port
   */
  public static void main(String[] args) {
    int portNum = 65535;
    ServerSocket Server;

    try {
      if (args.length > 0) {
        portNum = Integer.parseInt(args[0]);
        if (portNum < 1024 || portNum > 65535) {
          System.out.print("\nERROR " + "Invalid port number! Port should be between 1024 - 65535");
          System.exit(2);
        }
      }
      else {
        System.out.println("\nERROR "
            + "No port number provided! Port should be between 1024 - 65535");
        // System.exit(2);
        System.out.println("DEBUG OVERRIDE: USING PORT 65533..."); // comment out this line!
      }
      // Create server process.
      // Server srv = new Server(portNum);
      Server = new ServerSocket(portNum, 10, InetAddress.getByName("127.0.0.1"));

      while (true) {
        Socket connected = Server.accept();
        (new Server(connected)).start();
      }
    }
    catch (Exception e) {
      System.out.print("\nERROR MAIN" + e.getMessage());
      System.exit(2);
    }
  } // main()

  private void print(String s) { // an alias to avoid typing so much!
    System.out.print(s);
  }

  // this is a overridden method from the Thread class we extended from
  public void run() {
    // Begin new stuff.
    String courseSelection = null, currentLine = null, contentLength = null;

    try {

      System.out.println("The Client " + connectedClient.getInetAddress() + ":"
          + connectedClient.getPort() + " is connected");

      inFromClient = new BufferedReader(new InputStreamReader(connectedClient.getInputStream()));
      outToClient = new DataOutputStream(connectedClient.getOutputStream());

      currentLine = inFromClient.readLine();
      String headerLine = currentLine;
      StringTokenizer tokenizer = new StringTokenizer(headerLine);
      String httpMethod = tokenizer.nextToken();
      String httpQueryString = tokenizer.nextToken();

      System.out.println(currentLine);

      // GET request
      if (httpMethod.equals("GET")) {
        System.out.println("GET request");
        if (httpQueryString.equals("/")) {
          String responseString =
              Server.HTML_START + "<h2>Fall 2012 Final Examination Schedule</h2>"
                  + Server.MAIN_FORM + Server.HTML_END;
          sendResponse(200, responseString, false);
        }
        else {
          sendResponse(404, "<b>The Requested resource not found ...."
              + "Usage: http://127.0.0.1:5000</b>", false);
        }
      }
      // POST request
      else {
        System.out.println("POST request");
        do {
          currentLine = inFromClient.readLine();

          if (currentLine.indexOf("Content-Type: multipart/form-data") != -1) {
            String boundary = currentLine.split("boundary=")[1];
            // The POST boundary
            while (true) {
              currentLine = inFromClient.readLine();
              if (currentLine.indexOf("Content-Length:") != -1) {
                contentLength = currentLine.split(" ")[1];
                System.out.println("Content Length = " + contentLength);
                break;
              }
            }

            // Content length should be < 2MB
            if (Long.valueOf(contentLength) > 2000000L) {
              sendResponse(200, "File size should be < 2MB", false);
            }

            while (true) {
              currentLine = inFromClient.readLine();
              if (currentLine.indexOf("--" + boundary) != -1) {
                break;
              }
            }

            int index = 0;
            while (inFromClient.ready() && index < 3) {
              courseSelection = inFromClient.readLine();
              index++;
            }
            sendResponse(200, courseSelection, false);
          } // if
        }
        while (inFromClient.ready()); // End of do-while
      }// else
    }
    catch (Exception e) {
      e.printStackTrace();
    }

    // we are now inside our own thread separated from the gui.
    ServerSocket serversocket = null;
    // To easily pick up lots of girls, change this to your name!!!
    print("\nSimple HTTPServer v. 0000000000\nCoded by John MacMillan\n");
    // Pay attention, this is where things starts to cook!
    try {
      // print/send message to the guiwindow
      print("\nAttempting to bind to localhost on port " + Integer.toString(port) + "...");
      // make a ServerSocket and bind it to given port,
      serversocket = new ServerSocket(this.port);
    }
    catch (Exception e) { // catch any errors and print errors to gui
      print("\nFatal Error:" + e.getMessage());
      return;
    }
    print("OK!\n");
    // go in a infinite loop, wait for connections, process request, send response
    while (true) {
      print("\nReady, Waiting for requests...\n");
      try {
        // this call waits/blocks until someone connects to the port we
        // are listening to
        Socket connectionsocket = serversocket.accept();
        // figure out what ipaddress the client commes from, just for show!
        InetAddress client = connectionsocket.getInetAddress();
        // and print it to gui
        print(client.getHostName() + " connected to server.\n");

        // as the name suggest this method handles the http request, see further down.
        // abstraction rules
        // count++;
        // http_handler(input, output);
      }
      catch (Exception e) { // catch any errors, and print them
        print("\nError:" + e.getMessage());
      }
    } // go back in loop, wait for next request
  } // run()

  /**
   * Stripped down implementation of HTTP
   * 
   * @param input buffer object from connection for receiving data
   * @param output buffer object from connection for sending data
   */
  private void http_handler(BufferedReader input, DataOutputStream output) {
    int method = 0; // 1 get, 2 head, 0 not supported
    String http = new String(); // a bunch of strings to hold
    String path = new String(); // the various things, what http v, what path,
    String file = new String(); // what file
    String user_agent = new String(); // what user_agent
    try {
      // This is the two types of request we can handle
      // GET /index.html HTTP/1.0
      // HEAD /index.html HTTP/1.0
      String tmp = input.readLine(); // read from the stream
      String tmp2 = new String(tmp);
      tmp.toUpperCase(); // convert it to uppercase
      print(tmp);
      if (tmp.startsWith("GET")) { // compare it is it GET
        method = 1;
      } // if we set it to method 1
      if (tmp.startsWith("HEAD")) { // same here is it HEAD
        method = 2;
      } // set method to 2

      if (method == 0) { // not supported
        try {
          output.writeBytes(construct_http_header(501, 0));
          output.close();
          return;
        }
        catch (Exception e3) { // if some error happened catch it
          print("error:" + e3.getMessage());
        } // and display error
      }

      int start = 0;
      int end = 0;
      for (int a = 0; a < tmp2.length(); a++) {
        if (tmp2.charAt(a) == ' ' && start != 0) {
          end = a;
          break;
        }
        if (tmp2.charAt(a) == ' ' && start == 0) {
          start = a;
        }
      }
      path = tmp2.substring(start + 2, end); // fill in the path

    }
    catch (Exception e) {
      print("\nERROR " + e.getMessage());
    } // catch any exception

    // path do now have the filename to what to the file it wants to open
    if (path.equalsIgnoreCase("date") || path.equalsIgnoreCase("count")) {
      print("\nContent requested: Current " + path);
    }
    else {
      print("\nClient requested:" + new File(path).getAbsolutePath() + "\n");

    }
    FileInputStream requestedfile = null;
    if (path.startsWith("../")) {
      print("\nInvalid request. Client attempted ../");
      return;
    }

    try {
      // NOTE that there are several security consideration when passing
      // the untrusted string "path" to FileInputStream.
      // You can access all files the current user has read access to!!!
      // current user is the user running the javaprogram.
      // you can do this by passing "../" in the url or specify absoulute path
      // or change drive (win)

      // try to open the file,
      if (!path.equalsIgnoreCase("date") && !path.equalsIgnoreCase("count")) {
        requestedfile = new FileInputStream(path);
      }
    }
    catch (Exception e) {
      try {
        // if you could not open the file send a 404
        output.writeBytes(construct_http_header(404, 0));
        // close the stream
        output.close();
      }
      catch (Exception e2) {
      }
      print("\n ERROR " + e.getMessage());
    } // print error to gui

    // happy day scenario
    try {
      int type_is = 0;
      // find out what the filename ends with,
      // so you can construct a the right content type
      if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
        type_is = 1;
      }
      if (path.endsWith(".gif")) {
        type_is = 2;
        // write out the header, 200 ->everything is ok we are all happy.
      }
      if (path.endsWith(".htm") || path.endsWith(".html")) {
        type_is = 0;
      }
      if (path.equalsIgnoreCase("date")) {
        method = 4;
        type_is = 4;
      }
      if (path.equalsIgnoreCase("count")) {
        method = 5;
        type_is = 4;
      }

      output.writeBytes(construct_http_header(200, type_is));

      if (method == 4) {
        Date now = new Date();
        output.writeChars(now.toString());
      }
      else if (method == 5) {
        output.write(count);
      }
      // if it was a HEAD request, we don't print any BODY
      else if (method == 1) { // 1 is GET 2 is head and skips the body
        while (true) {
          // read the file from filestream, and print out through the
          // client-outputstream on a byte per byte base.
          int b = requestedfile.read();
          if (b == -1) {
            break; // end of file
          }
          output.write(b);
        }

      }
      // clean up the files, close open handles
      output.close();
      requestedfile.close();
    }

    catch (Exception e) {
    }
  }

  /**
   * Builds the HTTP Header for the response the headers job is to tell the browser the result of
   * the request.
   * 
   * @param return_code HTTP return code to provide in the header
   * @param file_type the type of file being returned
   * @return The completed HTTP Header.
   */
  // this method makes the HTTP header for the response
  // the headers job is to tell the browser the result of the request
  // among if it was successful or not.
  private String construct_http_header(int return_code, int file_type) {
    String s = "HTTP/1.0 ";
    // you probably have seen these if you have been surfing the web a while
    switch (return_code) {
    case 200:
      s = s + "200 OK";
      break;
    case 400:
      s = s + "400 Bad Request";
      break;
    case 403:
      s = s + "403 Forbidden";
      break;
    case 404:
      s = s + "404 Not Found";
      break;
    case 500:
      s = s + "500 Internal Server Error";
      break;
    case 501:
      s = s + "501 Not Implemented";
      break;
    }

    s = s + "\r\n"; // other header fields,
    s = s + "Connection: close\r\n"; // we can't handle persistent connections
    s = s + "Server: SimpleHTTPtutorial v0\r\n"; // server name

    // Construct the right Content-Type for the header.
    // This is so the browser knows what to do with the
    // file, you may know the browser dosen't look on the file
    // extension, it is the servers job to let the browser know
    // what kind of file is being transmitted. You may have experienced
    // if the server is miss configured it may result in
    // pictures displayed as text!
    switch (file_type) {
    // plenty of types for you to fill in
    case 0:
      s = s + "Content-Type: text/html\r\n";
      break;
    case 1:
      s = s + "Content-Type: image/jpeg\r\n";
      break;
    case 2:
      s = s + "Content-Type: image/gif\r\n";
    case 3:
      s = s + "Content-Type: application/x-zip-compressed\r\n";
    case 4:
      s = s + "Content-Type: text/plain\r\n";
    default:
      break;
    }

    // //so on and so on......
    s = s + "\r\n"; // this marks the end of the httpheader
    // and the start of the body
    // ok return our newly created header!
    return s;
  }

  /**
   * Sends a stripped down HTTP header response to the client.
   * 
   * @param statusCode Provisional response notifying client of server status.
   * @param responseString response message to the client.
   * @param isFile indicates if the response is a file.
   * @throws Exception
   */
  public void sendResponse(int statusCode, String responseString, boolean isFile) throws Exception {

    String statusLine = null;
    String serverdetails = "Server: Java HTTPServer";
    String contentLengthLine = null;
    String fileName = null;
    String contentTypeLine = "Content-Type: text/html" + "\r\n";
    FileInputStream fin = null;

    if (statusCode == 200)
      statusLine = "HTTP/1.1 200 OK" + "\r\n";
    else
      statusLine = "HTTP/1.1 404 Not Found" + "\r\n";

    if (isFile) {
      fileName = responseString;
      fin = new FileInputStream(fileName);
      contentLengthLine = "Content-Length: " + Integer.toString(fin.available()) + "\r\n";
      if (!fileName.endsWith(".htm") && !fileName.endsWith(".html"))
        contentTypeLine = "Content-Type: \r\n";
    }
    else {
      responseString = Server.HTML_START + responseString + Server.HTML_END;
      contentLengthLine = "Content-Length: " + responseString.length() + "\r\n";
    }

    outToClient.writeBytes(statusLine);
    outToClient.writeBytes(serverdetails);
    outToClient.writeBytes(contentTypeLine);
    outToClient.writeBytes(contentLengthLine);
    outToClient.writeBytes("Connection: close\r\n");
    outToClient.writeBytes("\r\n");
    outToClient.writeBytes(responseString);

    outToClient.close();
  }
} // class phhew caffeine yes please!